home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / UTILFILE / NJRAMD.LZH / NJRAMDX.ASM < prev    next >
Assembly Source File  |  1991-06-23  |  39KB  |  1,331 lines

  1. ; DEBUG   EQU TRUE
  2.  
  3. V286    EQU TRUE                        ; No XMS on 8088 PC's!
  4.  
  5. ;  Nifty James' Famous Expanded Memory Disk Drive
  6. ;  (C) Copyright 1987 by Mike Blaszczak.  All Rights Reserved
  7.  
  8. ;  Version 1.01  of  24 May 1987
  9. ;  Version 1.10  of  25 May 1987
  10. ;  Version 1.15  of  31 May 1987
  11. ;  Version 1.20  of  16 Oct 1987
  12.  
  13. ;  Modified Terje Mathisen 20 March 90
  14. ;  Version 1.02 of XMS-compatible NJRAMDX 14 Sep 1990
  15. ;  Version 1.40  of  23 Jun 1991 by Mike Blaszczak
  16.  
  17. ;  Shareware  $15     Please contribute!
  18.  
  19. ;  Assemble with
  20. ;    TASM NJRAMDX
  21. ;        (Comment out V286 EQU TRU for 8088 version.)
  22. ;    TLINK /t NJRAMDX, NJRAMDX.SYS
  23.  
  24. ;  --> DEVICE DRIVER FORMAT FILE <--
  25.  
  26. ; ---------------------------------------------------------------------------
  27.  
  28. ;   ASCII Characters
  29.  
  30. bell        equ    7        ; bell character
  31. tab        equ    9        ; tab character
  32. lf        equ    10        ; linefeed
  33. cr        equ    13        ; carriage return
  34. space        equ    32        ; space
  35. eos        equ    '$'        ; end of DOS string
  36.  
  37. ; ---------------------------------------------------------------------------
  38. ;   I/O Ports
  39.  
  40. Speak        equ    061h        ; speaker port
  41. SpeakMask    equ    011111110b    ; mask for speaker set bit
  42. SpeakToggle    equ    000000010b    ; toggle bit for the speaker
  43.  
  44. ; ---------------------------------------------------------------------------
  45. ;   DOS Calls
  46.  
  47. ; These are DOS functions used by the driver.
  48.  
  49. DisplayOut    equ    002h        ; call to print a single character
  50. PrintString    equ    009h        ; call to print a '$' string
  51. GetDOSVersion    equ    030h        ; call to get the DOS version #
  52.  
  53. ; ---------------------------------------------------------------------------
  54. ;  XMS Routines
  55.  
  56. ; These are the XMS functions that we use.  (These are specific functions
  57. ; of the XMS call.)
  58.  
  59. X_Counts    equ    08h        ; determine free/total mem
  60. X_Alloc        equ    09h        ; open, allocate, obtain handle ID
  61. X_Version    equ    00h        ; get the XMS version number
  62. X_Move        equ    0Bh        ; move an XMS block
  63.  
  64. ; ---------------------------------------------------------------------------
  65. ;  Driver Equates
  66.  
  67. ; This is the media descriptor byte.  Since our RAM drive is not 2 sided,
  68. ; does not have 8 sectors per track, and is not removable, we use 0F8h.
  69. ; At least, that's what the IBM DTR manual says.
  70.  
  71. MediaD        equ    0F8h
  72.  
  73. ; These are equates used by the driver.  They are all status and
  74. ; error flags, as defined in the DOS Technical Reference Manual.
  75.  
  76. ;                        FEDCBA9876543210 <- BIT NUMBERS
  77. errorflag    equ    01000000000000000b    ; error bit flag
  78. busystat    equ    00000001000000000b    ; busy status bit flag
  79. donestat    equ    00000000100000000b    ; done status bit flag
  80.  
  81. err_writeprot    equ    0        ; write protect violation
  82. err_badunit    equ    1        ; unknown unit number
  83. err_notready    equ    2        ; device not ready
  84. err_unknown    equ    3        ; unknown command
  85. err_CRC        equ    4        ; error CRC command
  86. err_reqlen    equ    5        ; bad request length
  87. err_seek    equ    6        ; seek failure
  88. err_badmedia    equ    7        ; bad media
  89. err_badsector    equ    8        ; sector not found
  90. err_badwrite    equ    10        ; write fault
  91. err_badread    equ    11        ; read fault
  92. err_general    equ    12        ; general failure
  93.  
  94. ; ---------------------------------------------------------------------------
  95. ;  Structure Definitions
  96.  
  97. ;  The structures defined here are used to find information in the
  98. ;  various request header formats.  Of course, being structures, they
  99. ;  don't take up space... they are used to define offsets for the
  100. ;  addressing of the request header.
  101.  
  102. rq    equ    es:bx            ; base address used in routines
  103.  
  104. ;  -- Request Header (General Format)
  105.  
  106. rhead    struc
  107.     rlen    db    ?    ; length of the structure
  108.     unitn    db    ?    ; unit number
  109.     command    db    ?    ; command code
  110.     status    dw    ?    ; status code (returned by us)
  111.         db    8 dup(?); reserved bytes
  112. rhead    ends
  113.  
  114.  
  115. ;  -- Request Header (INIT Command)
  116.  
  117. inithead    struc
  118.         db    (type rhead) dup (?)
  119.     units    db    ?    ; number of units
  120.     ndadro    dw    ?    ; ending address offset
  121.     ndadrs    dw    ?    ; ending address segment
  122.     bpboff    dw    ?    ; BPB offset pointer
  123.     bpbseg    dw    ?    ; BPB segment pointer
  124.     taglet    db    ?    ; drive tag letter
  125. inithead    ends
  126.  
  127. ;  -- Request Header (Media Check)
  128.  
  129. mediahead    struc
  130.         db    (type rhead) dup (?)
  131.     media    db    ?    ; our meida descriptor byte
  132.     change    db    ?    ; changed media flag
  133. mediahead    ends
  134.  
  135. ;  -- Request Header (Build BPB)
  136.  
  137. bbpbhead    struc
  138.         db    (type rhead) dup (?)
  139.         db    ?    ; media descriptor byte
  140.     baoff    dw    ?    ; transferr buffer address offset
  141.     baseg    dw    ?    ; transferr buffer address segment
  142.         dw    ?    ; BIOS parameter block pointer
  143.         dw    ?    ; BIOS parameter block pointer
  144. bbpbhead    ends
  145.  
  146. ;  -- Request Header (Read and Write)
  147.  
  148. rwhead        struc
  149.         db    (type rhead) dup (?)
  150.         db    ?    ; media descriptor byte
  151.     tbaoff    dw    ?    ; transferr buffer address offset
  152.     tbaseg    dw    ?    ; transferr buffer address segment
  153.     count    dw    ?    ; sector count
  154.     strtsec    dw    ?    ; starting sector number
  155. rwhead        ends
  156.  
  157.  
  158. ; With these headers defined as they are, access to the request header
  159. ; and command info fields is greatly simplified.  By setting ES:BX to
  160. ; point to the request header, the information can be easily referenced
  161. ; by using constructs such as
  162.  
  163. ;        mov    [rq.count],ax
  164. ;  or
  165. ;        mov    al,[rq.command]
  166.  
  167. ; Note that any part of the program can easily reference any particular
  168. ; command's structure, since the line
  169.  
  170. ;        db    (type rhead) dup (?)
  171.  
  172. ; makes all the command-specific structures "equivalent".
  173.  
  174. ; XMS Extended Memory Move structure
  175.  
  176. ExtendedMemoryMove     STRUC
  177.   NrOfBytes       dw 0,0
  178.   SourceHandle    dw 0
  179.   SourceOffset    dw 0,0
  180.   DestHandle      dw 0
  181.   DestOffset      dw 0,0
  182. ExtendedMemoryMove     ENDS
  183.  
  184. ; Check to see if this is the 286 version
  185.  
  186. ifdef  V286
  187.     .286
  188. ;    if1
  189.         %OUT    Enhanced processor version
  190. ;    endif
  191.     ifdef    PCL
  192. ;    if1
  193.         %OUT    for the PC's Limited 286/386
  194. ;    endif
  195.     endif
  196. else
  197. ;    if1
  198.         %OUT    Standard Version
  199. ;    endif
  200. endif
  201.  
  202. ;  This macro is used during debugging.  It prints a single character
  203. ; via the BIOS screen interface, and leaves the registers unchanged.
  204.  
  205. ifdef    DEBUG
  206.  
  207. ;    if1
  208.         %OUT  DEBUG Version
  209. ;    endif
  210.     PrintChar    macro    Char
  211.         ifdef    PCL
  212.  
  213.             push    ax
  214.             mov    al,Char
  215.             out    095h,al        ; put it digit 3 of smartvu
  216.             pop    ax
  217.  
  218.         else
  219.     
  220.             push    ax        ; save the regs
  221.             push    bx
  222.             push    dx
  223.             mov    ah,15
  224.             int    010h        ; get the current page
  225.             mov    al,Char
  226.             mov    ah,14        ; print the character
  227.             int    010h
  228.  
  229.             xor    dx,dx
  230.             mov    ah,0        ; also to printer
  231.             mov    al,Char
  232. ;            int    017h
  233.  
  234.             pop    dx
  235.             pop    bx        ;restore the regs
  236.             pop    ax
  237.  
  238.             endif
  239.             endm
  240.  
  241. else
  242.     PrintChar    macro    Char        ; if not debugging, blow it off
  243.             endm
  244. endif
  245.  
  246. ; ---------------------------------------------------------------------------
  247. ;  Public declarations for SYMDEB
  248.  
  249. ; These are public declarations included to allow SYMDEB to know where
  250. ; various lables and addresses are.  They are only needed for debugging,
  251. ; and serve no other useful purpose.
  252.  
  253. IF 1
  254.  
  255. PUBLIC NextPlace
  256. PUBLIC Attrib, JumpTable, TopCommand, RBPoint, RBPointOff, RBPointSeg, SaveSS
  257. PUBLIC SaveSP, StackTop, STRATPROC, Strategy
  258. PUBLIC INTPROC, Interrupt, FreakOut, IOCTLInput, ReadNoWait
  259. PUBLIC InputStatus, InputFlush, badcommand, BigLog, MC, MediaCheck
  260. PUBLIC BBPB, BuildBPB, BPBArray, OurBoot, OurBPB, SecSize, SecPerCluster
  261. PUBLIC RDirLen, DiskSize, SecPerFAT, BootCode
  262. PUBLIC RSEC, Read
  263. PUBLIC WSEC, Write
  264. PUBLIC TestValues, RangeError, SPEAKERCLICK, MakeClick
  265. PUBLIC SpeakerFlag, LastResident
  266. PUBLIC EatingWhite, GotOption
  267. PUBLIC NoBump, NotSilence, PagesLoop, LastDigit, NotPages, NotUseAll
  268. PUBLIC Unrecognized, EndOfLine, BigBust, ReTry
  269. PUBLIC GoodCombo, WipeOut
  270. PUBLIC CalcDiskFree, ClickOkay, MsgOkay, InitFail, GenFail, HowMuch
  271. PUBLIC RqdPages, MajorVersion, OurVolume, Banner, General
  272. PUBLIC NoEMMThere, EMMError, Init, NoMem, TooBig, BadOption, NoClicking
  273. PUBLIC Installed, DriveName, InstalledB, Installed2, UsedSpace, Bin2Dec
  274. PUBLIC Bin2DecLoop, Bin2DecDigit, WorkAreaL, WorkAreaH
  275.  
  276. ENDIF
  277.  
  278. ; ---------------------------------------------------------------------------
  279.  
  280. driver        segment    para public 
  281.         assume    cs:driver,ds:driver,es:driver,ss:driver
  282.  
  283.         org    0        ; drivers begin at zero
  284. firstplace    equ    this byte    ; this is the first byte
  285.  
  286. ; ---------------------------------------------------------------------------
  287. ;  Device Header
  288.  
  289. ; This area contains the header information.  It is used by DOS when loading
  290. ; the device driver, and it contains information used to describe the
  291. ; driver to the DOS environment.
  292.  
  293. NextPlace    dw    -1,-1        ; pointer to next driver
  294. Attrib        dw    00010000000000000b        ; attribute word
  295.             ;FEDCBA9876543210
  296.  
  297.                     ; device is non-ibm and block mode
  298.                     ; doesn't support IOCTL, is not
  299.                     ; a network device
  300.  
  301.         dw    offset Strategy        ; the strategy entry
  302.         dw    offset Interrupt    ; the interrupt entry
  303.         db    1,'NJTMDSK'        ;  Nifty James/TM' Disk!
  304.  
  305. ; ---------------------------------------------------------------------------
  306.  
  307.         ALIGN 2
  308.  
  309. JumpTable    label word
  310.  
  311. ; This area is a "Jump Table" that is used to dispatch the code.
  312. ; Only the functions marked with a "*" in their comment field
  313. ; are actually implemented.  (Since this is a block device, only
  314. ; some of the areas are actually used.)
  315.  
  316.     dw    offset    Init        ;  0 * initialize
  317.     dw    offset    MediaCheck    ;  1 * media check
  318.     dw    offset    BuildBPB    ;  2 * build BIOS parameter block
  319.     dw    offset    IOCTLInput    ;  3   I/O Control (Input)
  320.     dw    offset    Read        ;  4 * read from device
  321.     dw    offset    ReadNoWait    ;  5   read from device (nondest,
  322.                     ;    no wait, char only)
  323.     dw    offset    InputStatus    ;  6   input status
  324.     dw    offset    InputFlush    ;  7   flush pending input
  325.     dw    offset    Write        ;  8 * Write data
  326.     dw    offset    Write        ;  9 * Write data with Verify
  327. ;
  328. ;    dw    offset    OutputStat    ; 10   Output status
  329. ;    dw    offset    OutputFlush    ; 11   flush pending output
  330. ;    dw    offset    IOCTLOutput    ; 12   I/O Control (Output)
  331. ;    dw    offset    DeviceOpen    ; 13   Open Device
  332. ;    dw    offset    DeviceClose    ; 14   Close Device
  333. ;    dw    offset    Removeable    ; 15   Removable media check
  334. ;
  335.     ;  (The commands above 9 are all not implemented -- we don't
  336.     ;   make entries for them to optimize for space (and speed).
  337.     ;   The equate TopCommand must be set to the last used
  338.     ;   command code.)
  339.  
  340. TopCommand    equ    9        ; highest valid command
  341.  
  342. RBPoint        label    dword        ; Pointer to request buffer
  343. RBPointOff    dw    0        ; offset part
  344. RBPointSeg    dw    0        ; segment part
  345.  
  346. SaveSS        dw    0        ; save place for the SS register
  347. SaveSP        dw    0        ; save place for the SP register
  348.  
  349. R_EMM   ExtendedMemoryMove <>           ; EMM block for read requests
  350. W_EMM   ExtendedMemoryMove <>           ; EMM block for write
  351.  
  352. EMM_Vector      dd ?
  353.  
  354. ; ---------------------------------------------------------------------------
  355. ;  The local stack
  356.  
  357.         even            ; make the stack a word-aligned area
  358.         dw    128 dup ('TM')  ; HIMEM.SYS may need 256 byte stack!
  359. StackTop:
  360.  
  361. ; ---------------------------------------------------------------------------
  362. ;  Strategy Entry Point For the Device Driver
  363.  
  364. ;  This routine simply stores the pointer to the request header
  365. ;  so that request header has it.  That's all it does.  Really.
  366.  
  367. STRATPROC    proc    far
  368.  
  369. Strategy:
  370.     mov    [cs:RBPointOff],bx
  371.     mov    [cs:RBPointSeg],es    ; just store the pointer
  372.     ret                ; and get outta here!
  373.     ; (isn't it ironic that the shortest routine is called "Strategy"?)
  374. STRATPROC    endp
  375.  
  376. ; ---------------------------------------------------------------------------
  377. ;  Interrupt Entry Point For the Device Driver
  378.  
  379. ;  This routine executes the command contained in the passed request header.
  380. ;  DOS has called STRATEGY, and that routine stored a pointer to the request
  381. ;  header for our use.  We will construct our own stack area because the 
  382. ;  XMS driver uses a great deal of stack space. (Up to 256 bytes!)
  383.  
  384. INTPROC        proc    far
  385. Interrupt:    
  386. PrintChar 'D'
  387.                 push    ax                      ; TMA
  388.  
  389.         mov    [CS:SaveSS],ss        ; save the SS register
  390.         mov    [CS:SaveSP],sp        ; save the SP register
  391.  
  392.                 mov     ax, cs                  ;TMA mod
  393.         cli
  394.         mov    ss, ax
  395.         mov    sp, offset StackTop    ; initialize our stack
  396.         sti
  397.  
  398.     ifdef V286
  399.         pusha
  400.     else
  401.         push    bx        ; save the other regs
  402.         push    cx
  403.         push    dx
  404.         push    bp
  405.         push    si
  406.         push    di
  407.     endif
  408.  
  409.         pushf            ; and the flags
  410.         cld            ;  set the string direction up
  411.         push    es
  412.         push    ds
  413.  
  414.         mov    ds,ax        ; setup the data segment register
  415.  
  416.     ; Note that during calls we use DS to point to our local data
  417.     ; and ES to point to the request header.
  418.  
  419.         les    bx,[RBPoint]    ; get the request buffer
  420.         mov    al,[rq.command]    ;  get the command
  421.         cbw                     ; word for jump table
  422.  
  423. ; be sure that the command is in our range
  424.  
  425.         cmp    al,TopCommand    ; fifteen is the highest for us
  426.          ja    badcommand    ;  too high!    # UNSIGNED! TMA
  427.  
  428.                 shl    ax,1        ; (one word offset = 2 bytes)
  429.         mov    si,ax
  430.  
  431. ; fake a "short call" by setting the return address to the exit routine
  432.  
  433.     ifdef V286
  434.         push    offset BigLog
  435.         else
  436.         mov    ax,offset BigLog
  437.         push    ax
  438.         endif
  439.         mov    ax,donestat        ; assume OK
  440.         jmp    JumpTable[si]           ; and hop to it!
  441.  
  442. ; ---------------------------------------------------------------------------
  443. ;   We come here if we run into an XMS error.  We'll set the "General
  444. ;   Failure" flag, and return to MS-DOS
  445.  
  446. FreakOut:    mov    ax,(errorflag+err_general+donestat)
  447.                         ; general failure
  448.                         ; and error settings
  449.         jmp    short BigLog
  450.  
  451. ; ---------------------------------------------------------------------------
  452. ;   Ran into an unsupported command -  set the flag in the status word.
  453.  
  454. BadInit:        ; A second INIT call is a NO-NO
  455. IOCTLInput:
  456. ReadNoWait:    ; those table entries are invalid commands
  457. InputStatus:
  458. InputFlush:
  459.         pop    ax        ; (forget about the short call)
  460. badcommand:
  461.     mov    ax,(err_unknown+errorflag+donestat) ; an unknown command err
  462.  
  463. ; ---------------------------------------------------------------------------
  464. ;   This is the mass exit; everone splits through this point!  When we
  465. ;   arrive here, the AX reg will contain the word to be put into the
  466. ;   status word.  We'll do that:
  467.  
  468. BigLog:        
  469. PrintChar 'X'
  470.  
  471.         mov    [rq.status],ax
  472.  
  473. ;  Now, we just undo the registers.
  474.  
  475.         pop    ds        ; the seg regs
  476.         pop    es
  477.  
  478.         popf            ; the flags
  479.     ifdef V286
  480.         popa
  481.     else
  482.         pop    di
  483.         pop    si        ; and the data regs
  484.         pop    bp
  485.         pop    dx
  486.         pop    cx
  487.         pop    bx
  488.     endif
  489.  
  490. PrintChar 'd'
  491.         cli
  492.         mov    ss,[CS:SaveSS]
  493.         mov    sp,[CS:SaveSP]    ; restore the calling stack
  494.         sti
  495.  
  496.                 pop     ax              ; TMA
  497.         ret
  498.  
  499. INTPROC        endp
  500.  
  501. ; ---------------------------------------------------------------------------
  502. ;  MEDIA CHECK
  503.  
  504. ;  This command checks to see if the media has been removed and replaced.
  505. ;  Since a RAM drive is non-removable media, this command will always
  506. ;  return a "false".
  507.  
  508. MC        proc    near
  509. PrintChar 'M'
  510. MediaCheck:
  511.         mov    [rq.change],1    ; media has not been changed
  512. PrintChar 'm'
  513.         ret            ; return to leave
  514. MC        endp
  515.  
  516. ; ---------------------------------------------------------------------------
  517. ;  BUILD BIOS PARAMETER BLOCK
  518.  
  519. ;  This command simply "builds" a BPB by telling DOS where it is located.
  520.  
  521. BBPB        proc    near
  522. BuildBPB:
  523. PrintChar 'P'
  524.         mov    [rq.bpboff],OFFSET OurBPB          ; the offset
  525.         mov    [rq.bpbseg],cs            ; in our CS
  526. PrintChar 'p'
  527.         ret
  528.  
  529. BPBArray    dw    offset OurBPB
  530.  
  531. OurBoot:    db    0,0,0
  532.         db    'TerjeXMS'    ;  whodat?
  533.         
  534. OurBPB:
  535. SecSize        dw    512        ; standard DOS sector size
  536. SecPerCluster    db    1        ; sectors per allocation unit
  537.         dw    1        ; number of reserved sectors
  538.         db    1        ; number of copies of the FAT
  539. RDirLen        dw    32        ; number of root directory sectors TMA
  540.  
  541. DiskSize    dw    1024        ; number of sectors on the disk
  542.         db    MediaD        ; (media descriptor)
  543. SecPerFAT    dw    1        ; number of sectors per FAT
  544.  
  545.         dw    8        ; sectors per track
  546.         dw    1        ; number of heads
  547.         dw    0        ; number of hidden sectors
  548. BootCode:
  549.  
  550. OurBootLen    equ    this byte - OurBoot
  551.  
  552. BBPB        endp
  553.  
  554.  
  555. ; ---------------------------------------------------------------------------
  556. ;  WRITE
  557.  
  558. ;  This command writes the specified number of sectors starting at the
  559. ;  given sector.  It returns the number of sectors actually written.
  560. ;  Errors are returned if the sector is out of range, or if the number
  561. ;  of sectors
  562. ;  is past the end of the disk.  (The error checking is done in the
  563. ;  TestValues procedure.)  This procedure doesn't do much itself.  It's
  564. ;  body is mostly an XMS move call.
  565.  
  566. WSEC        proc    near
  567. Write:
  568.     ifdef PCL
  569.         mov    al,'N'
  570.         out    097h,al
  571.         mov    al,'J'
  572.         out    096h,al        ; display "NJ-W" on Smart-Vu
  573.         mov    al,'-'
  574.         out    095h,al
  575.         mov    al,'W'
  576.         out    094h,al
  577.     endif
  578.  
  579. PrintChar 'W'
  580.  
  581.                 mov     si, offset W_EMM
  582.  
  583.                 call    TestValues
  584.  
  585.                 mov     [si.DestOffset+1],ax
  586.  
  587.                 mov     [si.SourceOffset],cx
  588.                 mov     [si.SourceOffset+2],dx
  589.  
  590.                  jmp     Do_XMS_Transfer
  591.  
  592. WSEC        endp
  593.  
  594. ; ---------------------------------------------------------------------------
  595. ;  READ
  596.  
  597. ;  This command reads the specified number of sectors starting at the 
  598. ;  given sector.  It returns the number of sectors actually read.  Errors
  599. ;  are returned if the sector is out of range, or if the number of sectors
  600. ;  is past the end of the disk.  (The error checking is done in the
  601. ;  TestValues procedure.)  This procedure doesn't do much itself.  It's
  602. ;  body is mostly a call for an XMS move instruction.
  603.  
  604. RSEC        proc    near
  605. Read:
  606.  
  607.     ifdef PCL
  608.         mov    al,'N'
  609.         out    097h,al
  610.         mov    al,'J'
  611.         out    096h,al        ; display "NJ-R" on Smart-Vu
  612.         mov    al,'-'
  613.         out    095h,al
  614.         mov    al,'R'
  615.         out    094h,al
  616.     endif
  617.  
  618. PrintChar 'R'
  619.  
  620.                 mov     si, offset R_EMM
  621.  
  622.                 call    TestValues      ; AX = Sec# Shl 1, DX:CX = TBA
  623.  
  624.                 mov     [si.SourceOffset+1],ax
  625.  
  626.                 mov     [si.DestOffset],cx
  627.                 mov     [si.DestOffset+2],dx
  628.  
  629. Do_XMS_Transfer:                        ; Common code for Read & Write
  630.                 mov     ah, X_Move
  631.                 call    [EMM_Vector]
  632.                 or      ax,ax
  633.                  jz     XMS_Transfer_Error
  634.  
  635.                 mov     ax,donestat    ; OK!
  636.  
  637. ReadClick label byte
  638.         jmp     MakeClick    ; finish clicking
  639.  
  640. XMS_Transfer_Error:
  641.                 mov    ax,(err_general+errorflag+donestat)
  642.                 mov     [rq.Count],0    ; Tell DOS nothing done!
  643.  
  644. WriteClick label byte
  645.          jmp     MakeClick    ; finish clicking
  646.         
  647. RSEC        endp
  648.  
  649.  
  650. RangeError:    pop    ax        ; forget our return address
  651.         mov    ax,err_badsector + errorflag + donestat ; TMA
  652.         mov    [rq.count],0    ; no sectors were read, you know
  653.         ret            ;  return back to the dispatcher
  654.  
  655. TestValues      proc near
  656.  
  657.                 mov     ax,[rq.strtsec]
  658.                 mov     dx,ax
  659.                 mov     cx,[rq.count]
  660.                 add     dx,cx
  661.                  jc     RangeError
  662.                 cmp     dx,DiskSize
  663.                  ja     RangeError
  664.  
  665.                 shl     ax,1            ; 512 byte/sector = 256 Shl 1
  666.                 shl     cx,1            ; NrOfSectors Shl 9
  667.                 mov     [si.NrOfBytes+1],cx
  668.                 mov     cx,[rq.tbaoff]
  669.                 mov     dx,[rq.tbaseg]
  670.  
  671. TestValues      endp
  672.  
  673. ; ---------------------------------------------------------------------------
  674. ;  This is a local procedure that clicks the speaker transparently.  It
  675. ;  is executed at the end of the TestValues procedure, which is excuted
  676. ;  at the very beginning of the "READ" and "WRITE" functions.  It is
  677. ;  also JMP'd to at the end of the READ and WRITE routines, and the RET
  678. ;  at the end of this procedure will return to the caller of the READ
  679. ;  and WRITE functions.  (Saves 2 bytes and a bunch of clocks, hey.)
  680.  
  681. ;  From here modified by TMA: Uses self-modifying code to decrease the size
  682.  
  683. ;  If no clicks, the init call will modify the PUSHF to a RETN, and save
  684. ;  the rest of the proc from staying resident. The JMPs from READ & WRITE
  685. ;  to this proc will also be patched into RETNs. This way, we don't have to
  686. ;  test the SpeakerFlag at each call.
  687.  
  688. SPEAKERCLICK    proc    near
  689.  
  690. MakeClick:    pushf
  691.  
  692.         push    ax            ; yes, save the accumulator
  693.         in    al,Speak
  694.         and    al,SpeakMask        ; mask out the bit we don't need
  695.         xor    al,SpeakToggle        ; toggle the control bit
  696.                 jmp     $+2                     ; For fast PC's
  697.         out    Speak,al        ; and re-output it
  698.         pop    ax            ; retrieve the accumulator
  699.  
  700.                 popf
  701. NearRet label byte                      ; TMA Use this to get RETN opcode
  702.         ret            ; return to the caller
  703.  
  704. SPEAKERCLICK    endp
  705.  
  706. ; ---------------------------------------------------------------------------
  707. ;  This label marks the last byte of the device driver that actually
  708. ;  remains resident.  This driver takes less than 800 bytes, guaranteed.
  709.  
  710. LastResident    dw      $               ; Current End Of Program
  711.  
  712. SpeakerFlag    db    1        ; one if we should be ticking
  713.  
  714.                 JUMPS                   ; Non-critical code
  715.  
  716. ; ---------------------------------------------------------------------------
  717. ;  INITIALIZE
  718.  
  719. ;  This command sets up the internal data used by NJRAMD.  The procedure
  720. ;  sets the XMS to get the number of kB that the user requests.  (The
  721. ;  information following the specification in the CONFIG.SYS file is
  722. ;  parsed to find the user parameters.  See the NJFRAMD.DOC file to find
  723. ;  the format of the CONFIG information.)  The procedure requests memory
  724. ;  from the XMS driver
  725.  
  726. Init:
  727. PrintChar 'I'
  728.         mov    dx,offset Banner
  729.         mov    ah,PrintString        ; show our copyright!
  730.         int    21h
  731.  
  732.         mov    ah,GetDOSVersion    ; get the DOS version
  733.         int    21h
  734.         mov    [MajorVersion],al
  735.  
  736.                 mov     ax,4300h
  737.                 int     2Fh                     ; Is an XMS driver loaded?
  738.                 cmp     al,80h
  739.         mov    dx,offset NoEMMThere    ; point to our error
  740.          jne    InitFail                ; No XMS driver!
  741.  
  742. PrintChar '1'
  743.                 ; the XMS is present.  It's okay! Get EMM_Vector to call
  744.  
  745.                 mov     ax,4310h
  746.                 int     2Fh
  747.                 mov     word ptr [EMM_Vector],bx
  748.                 mov     word ptr [EMM_Vector+2],es
  749.  
  750.         mov    ah,X_Counts        ; get count of available
  751.         call    [EMM_Vector]        ; memory
  752. ; QEMM 5.1    or    bl,bl
  753. ;         jl    GenFail         ; general failure?
  754.  
  755. PrintChar '2'
  756.         cmp    ax,16        ; At least 16 kB?
  757.         mov    dx,offset NoMem    ; print error
  758.          jb     InitFail
  759.  
  760. PrintChar '3'
  761.                 mov    [HowMuch],ax        ; remember how much is left
  762.  
  763.     ; We will now attempt to parse the line of the CONFIG.SYS
  764.     ; file to see if any of our options are on it.
  765.  
  766.         les    bx,[RBPoint]        ; get pointer to header
  767.         les    si,es:[bx+18]        ; get pointer to commands
  768.  
  769. EatingWhite:    lods byte ptr es:[si]        ; get the next byte
  770.         cmp    al,cr            ; is it a carriage return?
  771.          je    EndOfLine
  772.                 cmp     al,'0'
  773.          jb    NotPages
  774.                 cmp     al,'9'
  775.                  ja     NotPages
  776.  
  777.     ; We will handle the pages option by reading the command line until
  778.     ; a non-numeric character.  The resulting number will be the number
  779.     ; of kB that the user requested.
  780.  
  781.         xor    dx,dx        ; zero the result
  782.                 sub     al,'0'
  783.                 mov     dl,al
  784.  
  785. PagesLoop:    lods    byte ptr [es:si]; get the character
  786.         cmp    al,'0'        ; is it a number?
  787.          jb    LastDigit    ;  nope!
  788.         cmp    al,'9'        ; is it a number?
  789.          ja    LastDigit    ;  note!
  790.  
  791.         push    ax        ; save the digit temporarily
  792.         mov    ax,10
  793.         mul    dx        ; multiply it out
  794.         pop    dx        ; pop the digit into dx
  795.  
  796.         and    dx,0Fh        ; make a decimal digit of it
  797.         add    dx,ax        ; add it into the sum
  798.         jmp    short PagesLoop
  799.  
  800. LastDigit:    mov    [RqdPages],dx    ; save requested number of pages
  801.         cmp    dx,16        ; is the requested # of KB < 16
  802.          jb    BadPages    ;  yeah!  can't have that
  803.         cmp    al,cr        ; was that last char a CR?
  804.          je    EndOfLine    ;  yes! end of the parse
  805.          jne    EatingWhite    ;  no, go back for more parsing
  806.  
  807. BadPages:    mov    dx,offset TooSmall
  808. BadPages2:    jmp    InitFail
  809.  
  810. NotPages:
  811.         cmp    al,'-'            ; is it an option marker?
  812.          je    GotOption        ; yeah! go process it!
  813.         cmp    al,'/'
  814.          jne    EatingWhite        ; no... go back for more
  815.  
  816.     ; We are now pointing at the text of an option.  We will
  817.     ; get the option into the al to see exactly what it is, and we
  818.     ; will then act accordningly.
  819.  
  820. GotOption:    lods byte ptr es:[si]        ; get the option
  821.         cmp    al,'a'            ; bump it to upper case?
  822.          jl    NoBump            ;  no need to
  823.         cmp    al,'z'
  824.          jg    NoBump            ;  no need to
  825.  
  826.         sub    al,('a' - 'A')        ; make it lower case
  827.         
  828. NoBump:        cmp    al,'S'            ; is it a silence option?
  829.          jne    NotSilence        ;   no...
  830.         mov    SpeakerFlag,0        ; yes, it is.  Reset the option!
  831.          jmp    EatingWhite        ; and eat up until end of
  832.                         ;  this option
  833.  
  834. NotSilence:
  835.                 cmp    al,'A'            ; is it use all memory?
  836.          jne    NotUseAll
  837.  
  838.         mov    ax,[HowMuch]
  839.         mov    [RqdPages],ax        ; request them all (KB)
  840.          jmp    EatingWhite
  841.  
  842. NotUseAll:
  843. Unrecognized:    mov    dx,offset BadOption    ; don't install
  844.          jmp    InitFail
  845.  
  846.  
  847. EndOfLine:    ; The parsing is done!  We will now check to see if the
  848.         ; requested size is bigger than the available memory.
  849.  
  850. PrintChar 'e'
  851.         mov    ax,[RqdPages]        ; is the reqested amount
  852.         cmp    ax,[HowMuch]        ;  greater than available?
  853.         mov    dx,offset TooBig    ; error msg
  854.          ja      InitFail        ; yes, abort
  855.  
  856. ; Now, we'll try to allocate that many kB.  If the user
  857. ; didn't specify a number of kB, the default is 512
  858.  
  859. PrintChar '4'
  860.         mov    dx,ax
  861.         mov    ah,X_Alloc    ; open a new handle of (DX) kB
  862.                 call    [EMM_Vector]
  863.         or    ax,ax
  864.          jz    GenFail
  865.  
  866. PrintChar '5'
  867.                 mov    [R_EMM.SourceHandle],dx ; save the handle for later
  868.                 mov     [W_EMM.DestHandle],dx
  869.  
  870.     ; We will now setup the information in the BPB to reflect the
  871.     ; status of the RAM drive.  First, we'll store the DiskSize.
  872.  
  873.         mov    ax,[RqdPages]        ; get number of pages
  874.         shl    ax,1                    ; 2 sectors in one KB
  875. ;                add     ax,7
  876. ;                and     ax, NOT 7               ; Should be divisible by 8!
  877.         mov    [DiskSize],ax        ; store it in BPB
  878.  
  879.     ; Now, we'll figure out how many entries there will be in the
  880.     ; root directory.  We will allow 1 root directory entry for
  881.     ; each 2k of storage that the disk has.  We won't allow more
  882.     ; than 512 root dir entries, though.
  883.  
  884.     ifdef    V286
  885.         shr    ax,2
  886.     else
  887.         shr    ax,1            ; figure out length of
  888.         shr    ax,1            ; root directory
  889.     endif
  890.         cmp    ax,512            ;  1 entry per 2k of storage
  891.          jb    BigBust            ;  up to 512
  892.  
  893.         mov    ax,512
  894.  
  895. BigBust:    add    ax,31            ; make sure it's a multiple
  896.         and    ax,not 31        ; of 32  (round it up)
  897.         mov    [RDirLen],ax
  898.  
  899.     ; Since we use a 12-bit FAT, we must have 4087 clusters or less.
  900.     ; We will start with a 1024-byte cluster, and double the cluster
  901.     ; size until we have enough FAT space. A user must
  902.     ; configure about 3.75 megabytes of memory as a RAM drive to
  903.     ; cause the program to use 2048-byte clusters... otherwise, the
  904.     ; drive will have 1024-byte clusters.
  905.  
  906.         mov    cx,2            ; Two clusters per sector
  907.                         ;  for starters.
  908.  
  909. ReTry:        mov    ax,[DiskSize]        ; get the disk size
  910.         xor    dx,dx
  911.         div    cx            ; AX = (DiskSize/SPC)
  912.         cmp    ax,4087            ; is it less than 4087?
  913.          jb    GoodCombo        ;  yeah!
  914.         shl    cx,1            ; no. double the SPC and
  915.          jnc    ReTry            ; try it again
  916.                  jmp    GenFail                 ; 64kB cluster is an error!
  917.  
  918. GoodCombo:    mov    [SecPerCluster],cl    ; save SPC number
  919.  
  920. PrintChar '6'
  921.  
  922.     ; AX still is set to the number of clusters on the disk.  Very
  923.     ; useful number, you know.  We will find now the amount of FAT
  924.     ; space that is needed.
  925.  
  926.         mov    bx,ax        ; ax = clustsers
  927.         add    ax,ax        ; ax = 2*(clusters)
  928.         add    ax,bx        ; ax = 3*(clusetrs)
  929.  
  930.                 inc     ax              ; In case of odd # of clusters  TMA
  931.  
  932.         shr    ax,1        ; ax = 1.5*(clusters)
  933.  
  934.                 add     ax,511          ; To round up                   TMA
  935.  
  936.         xor    dx,dx        ;      (FAT Length)
  937.         mov    cx,512        ; AX =    ----------------
  938.         div    cx        ;       (BytesPerSector)
  939.  
  940.                 mov    [SecPerFAT],ax    ; store it in the BPB
  941.  
  942.     ; The BPB is now set up properly.  We will now "format" the
  943.     ; RAM disk.  First, we will have to set all the RAM area to
  944.     ; zero.  (Even on extremely large "drives", this doesn't take
  945.     ; very long.)
  946.  
  947.                 mov     di, OFFSET InitBuffer   ; Make a block of ZERO's
  948.                 push    cs
  949.                 pop     es
  950.                 mov     cx, 512
  951.                 xor     ax,ax
  952.                 rep     stosw
  953.  
  954.         mov    cx,[DiskSize]    ; get number of sectors on disk
  955.                 shr     cx,1            ; CX = # of KB
  956.                 mov     si, OFFSET W_EMM; Use W_EMM for all writing
  957.  
  958. ;                mov     [si.DestOffset+1],0    ; Init to 0
  959.                 mov     [si.NrOfBytes],1024
  960.                 mov     [si.SourceOffset], OFFSET InitBuffer
  961.                 mov     [si.SourceOffset+2], CS
  962.  
  963. WipeOut:
  964.                 mov     ah, X_Move
  965.                 call    [EMM_Vector]
  966.                 or      ax,ax
  967.          jz    GenFail
  968.  
  969.             add     [si.DestOffset+1],4     ; 4 * 256 = 1024 = 1 kB
  970.          loop    WipeOut        ; if more, go back
  971.  
  972. PrintChar '7'
  973.  
  974.     ; Now that everything is zeroed, we will copy the pseudo-boot
  975.     ; sector that we have.  DOS uses some of this information while
  976.     ; reading and writing the disk, so we set it up there.
  977.  
  978.                 mov     [si.DestOffset+1],0    ; Init to 0
  979.                 mov     [si.NrOfBytes], (OurBootLen + 1) AND 0FFFEh
  980.                 mov     [si.SourceOffset], OFFSET OurBoot
  981.  
  982.                 mov     ah,X_Move
  983.         call    [EMM_Vector]
  984.                 or      ax,ax
  985.                  jz     GenFail
  986.  
  987. PrintChar '8'
  988.  
  989.     ; The boot sector has been written in.  We will now set up
  990.     ; the FAT.  This task is rather simplified, since we only
  991.     ; have one copy of the FAT.
  992.  
  993.         mov    byte ptr [InitBuffer],MediaD
  994.         mov    word ptr [InitBuffer+1],0FFFFh
  995.  
  996.                 mov     [si.DestOffset+1],2     ; 2 * 256 = 1 sector
  997.                 mov     [si.NrOfBytes], 4
  998.                 mov     [si.SourceOffset], OFFSET InitBuffer
  999.  
  1000.                 mov     ah,X_Move
  1001.         call    [EMM_Vector]
  1002.                 or      ax,ax
  1003.                  jz     GenFail
  1004.  
  1005. PrintChar '9'
  1006.  
  1007.     ; Now, we will figure out where the first directory sector is.
  1008.     ; *WARNING* - This code assumes that there is only one copy of
  1009.     ; the FAT, and that there is one reserved sector.  If ya change
  1010.     ; the drive to have 2 copies of the FAT, or modify it to have
  1011.     ; reserved sectors (for whatever reason you'd wanna do that),
  1012.     ; you'll have to change this code fragment!
  1013.  
  1014.         mov    ax,[SecPerFAT]
  1015.         inc    ax        ; AX = first dir sector
  1016.                 shl     ax,1            ; * 2 for 256 byte blocks
  1017.                 mov     [si.DestOffset+1],ax
  1018.                 mov     [si.NrOfBytes], (OurVolumeLen + 1) AND 0FFFEh
  1019.                 mov     [si.SourceOffset], OFFSET OurVolume
  1020.  
  1021.                 mov     ah,X_Move
  1022.         call    [EMM_Vector]
  1023.                 or      ax,ax
  1024.                  jz     GenFail
  1025.  
  1026. ; Initialize W_EMM.NrOfBytes back to Zero
  1027.  
  1028.                 mov     [si.NrOfBytes],0
  1029. PrintChar '!'
  1030.  
  1031.     ; Phew!  Now the whole thing is done!  We will show the user
  1032.     ; what has been done.  First, we will figure out what device
  1033.     ; tag that we have.  We will tell the user about it.  DOS versions
  1034.     ; earlier than 3.00 don't let us know what our device tag is,
  1035.     ; so we can't tell the user.
  1036.  
  1037. ; TMA:  First, check if not clicking, if so disable that part:
  1038.  
  1039.                 cmp     SpeakerFlag, 0        ; is there clicking?
  1040.          jne    UsingClick
  1041.  
  1042.                 mov     al, [NearRet]
  1043.                 mov     byte ptr [MakeClick], al ; RETN opcode    TMA
  1044.                 mov     [ReadClick], al
  1045.                 mov     [WriteClick], al
  1046.  
  1047.                 mov     [LastResident], offset MakeClick + 1  ; Reduce RAM TMA
  1048.  
  1049. UsingClick:    
  1050.         les    bx,[RBPoint]    ; point to the header, again
  1051.         mov    al,[rq.taglet]    ; get the tag letter
  1052.         add    al,'A'        ; change it to a capital drive letter.
  1053.         mov    [DriveName],al
  1054.  
  1055.         mov    bx,[LastResident] ; calculate used size
  1056.         xor    ax,ax
  1057.         mov    si,offset UsedSpace
  1058.         call    Bin2Dec            ; store it in the messgae
  1059.  
  1060.         mov    ah,X_Counts        ; find amount of space left
  1061.                 call    [EMM_Vector]
  1062.         or    bl,bl
  1063.          jl    GenFail
  1064.  
  1065. PrintChar '"'
  1066.                 mov     cl,6        ; AX:BX = (Space left SHL 16) SHR 6 = KB
  1067.                 xor     bx,bx           ; Zero low word
  1068. @@1:
  1069.                 shr     ax,1
  1070.                 rcr     bx,1
  1071.                  loop   @@1
  1072.  
  1073.         mov    si,offset Installed2
  1074.         call    Bin2Dec        ; put it into the message!
  1075.  
  1076.         mov    bx,[DiskSize]    ; get sectors of disk space
  1077.         sub    bx,[SecPerFAT]    ; subtract space used by the FAT
  1078.  
  1079.         mov    ax,[RDirLen]    ; get the entries in the root dir
  1080.     ifdef V286
  1081.         shr    ax,4
  1082.     else
  1083.         mov    cl,4
  1084.         shr    ax,cl        ; divide by # of entries per sec
  1085.     endif
  1086.         sub    bx,ax        ; subtract some more
  1087.         dec    bx        ; and adjust down for boot sector
  1088.  
  1089.         mov    cx,9        ; multiply the answer by 512
  1090.         xor    ax,ax        ; zero the high side
  1091. CalcDiskFree:    shl    bx,1        ; shift low side up
  1092.         rcl    ax,1        ;  shift high side over, with carry
  1093.          loop    CalcDiskFree
  1094.  
  1095.         mov    si,offset Installed
  1096.         call    Bin2Dec        ; store it in the message
  1097.  
  1098.         cmp     [SpeakerFlag], 0  ; is there clicking?
  1099.          jne    ClickOkay
  1100.  
  1101.         mov    ah,PrintString
  1102.         mov    dx,offset NoClicking    ; tell the user that it's
  1103.         int    21h            ; been disabled.
  1104.  
  1105. ClickOkay:
  1106.         cmp    [MajorVersion],3    ; is it version three+?
  1107.          jae    MsgOkay
  1108.  
  1109.         mov    [DriveName],eos   ; TMA Ver 2.x, so no drive letter
  1110.     
  1111. MsgOkay:    mov    dx,offset Installed    ;print part one of 
  1112.         mov    ah,PrintString        ;  installed! message
  1113.         int    21h
  1114.  
  1115.         mov    dx,offset InstalledB    ;print part two
  1116.         int    21h
  1117.  
  1118.         les    bx,[RBPoint]        ; get that pesky pointer
  1119.  
  1120.         mov    ax, [LastResident]     ; show DOS where we end  TMA
  1121.         mov    [rq.ndadro],ax        ;   offset
  1122.                                 ; show DOS were we end
  1123.         mov    [rq.ndadrs],cs        ;   segment
  1124.         mov    [rq.bpbseg],cs        ; show DOS the BPB array
  1125.  
  1126.         mov    [rq.units],1        ; we installed one unit
  1127.         mov    [rq.bpboff],OFFSET BPBArray ; BPB array offset
  1128.  
  1129.                 mov     [JumpTable], OFFSET BadInit ; In case second INIT call
  1130.  
  1131.         xor    ax,ax        ; no return value
  1132.         ret
  1133.  
  1134. ; ---------------------------------------------------------------------------
  1135. ;  Init failure
  1136.  
  1137. ;  We will come here if there is a failure during the initialization
  1138. ;  of the driver.  We print a message letting the user know why we can't
  1139. ;  install, and we then zero ourselves out so that DOS doesn't waste any
  1140. ;  memory on us.
  1141.  
  1142. InitFail:    push    dx        ; save the specific error
  1143.         mov    dx,offset General
  1144.         mov    ah,PrintString
  1145.         int    21h
  1146.  
  1147.         pop    dx        ; now print specific error
  1148.         mov    ah,PrintString
  1149.         int    21h
  1150.  
  1151.         les    bx,[RBPoint]    ; point to the request header
  1152.         mov    ax,cs
  1153.  
  1154.         mov    [rq.ndadrs],ax    ; ending address is zero
  1155.  
  1156.         xor    ax,ax        ;  because no memory is taken
  1157.         mov    [rq.ndadro],ax    ;  since we failed
  1158.         mov    [rq.units],al    ; no units, either
  1159. PrintChar 'i'
  1160.         ret
  1161.  
  1162. ; ---------------------------------------------------------------------------
  1163. ;  General Failure
  1164.  
  1165. ;  There was an EMM Failure during the installation.  If such is the case,
  1166. ;  we will terminate with an error message, and then go to the regular
  1167. ;  fail routine.
  1168.  
  1169. GenFail:    mov    dx,offset EMMError
  1170.         jmp    short InitFail
  1171.  
  1172.  
  1173. ; ---------------------------------------------------------------------------
  1174. ;  Transient Data Area
  1175.  
  1176. ;  The TDA contains the variables used by the Initialization segment of
  1177. ;  the device driver.  It doesn't stay resident.
  1178.  
  1179. HowMuch        dw    ?        ; amount of free EMS, in pages
  1180. RqdPages    dw    512        ; amount of pages requested
  1181.                     ;    (512k is the default)
  1182. MajorVersion    db    3        ; the DOS major version number
  1183.  
  1184. MTIME   MACRO h,m,s
  1185.         dw  (h SHL 11) + (m SHL 5) + (s SHR 1)
  1186.         ENDM
  1187.  
  1188. MDATE   MACRO y,m,d
  1189.         dw  ((y-80) SHL 9) + (m SHL 5) + d
  1190.         ENDM
  1191.  
  1192. OurVolume    db    'Terjes_Disk'    ; 11-byte volume name
  1193.         db    000001000b    ;  volume label attribute
  1194.         db    10 dup (0)    ; reserved space
  1195.             ;FEDCBA9876543210b
  1196. ; File Time & Date
  1197.         MTIME 23 08 40          ; 23:08:40
  1198.         MDATE 90 09 30          ; 90-09-30
  1199.         db    6 dup (0)    ; more reserved space
  1200.  
  1201. OurVolumeLen    equ    $ - OFFSET OurVolume
  1202.  
  1203. ; ---------------------------------------------------------------------------
  1204. ;  Messages
  1205.  
  1206. ;  These are messages that are used by the initialization section of the
  1207. ;  driver.
  1208.  
  1209. Banner    label byte
  1210.   db cr,lf
  1211.   db 'NJ/TMa Ram Disk (C) 1987 by Mike Blaszczak',cr,lf
  1212.   ifdef V286
  1213.     ifdef PCL
  1214.       db "PC's Limited "
  1215.     else
  1216.       db '286 '
  1217.     endif
  1218.   endif
  1219.   db 'XMS Version 1.02 (C) Terje Mathisen ',??time,' ',??date,cr,lf
  1220.  
  1221.   db  cr,lf,eos
  1222.  
  1223. General        db    'Device not installed.',cr,lf,eos
  1224.  
  1225. NoEMMThere    db    'The XMS driver is not installed.',cr,lf,lf,eos
  1226.  
  1227. EMMError    db    'XMS failure during installation.',cr,lf,lf,eos
  1228.  
  1229. NoMem        db    'No free XMS Memory.',cr,lf,lf,eos
  1230.  
  1231. TooBig        db    'Requested size too big to fit.',cr,lf,lf,eos
  1232.  
  1233. TooSmall    db    "Can't have disk size < 16kB.",cr,lf,lf,eos
  1234.  
  1235. BadOption    db    'Unrecognized option encountered.',cr,lf,lf,eos
  1236.  
  1237. NoClicking    db    'Clicking suppressed.',cr,lf,eos
  1238.  
  1239. ; "Installed" marks the beginning of the information that is printed
  1240. ; if the device is successfully installed.  The beginning of each
  1241. ; line has eight spaces, which are filled with the information by the
  1242. ; BIN2DEC procedure.  There is then one more space, so that the end
  1243. ; of the number doesn't bump the first word... thus, a total of nine
  1244. ; spaces begin the Installed, Installed2, and UsedSpace labels.
  1245.  
  1246. Installed    db    '         bytes available on RAM drive '
  1247. DriveName    db    '_:.',eos
  1248.  
  1249. InstalledB    db    cr,lf
  1250. Installed2    db    '         bytes left in XMS storage.',cr,lf
  1251. UsedSpace    db    '         bytes of standard DOS memory were'
  1252.         db    ' taken.',cr,lf,lf
  1253.         db    eos
  1254.  
  1255. ; ---------------------------------------------------------------------------
  1256. ;  Init Subroutines
  1257.  
  1258. ;  The following area contains subroutines used by the INIT procedure of
  1259. ;  the device driver.  They aren't kept in memory after the device has been
  1260. ;  installed.
  1261.  
  1262. ; ---------------------------------------------------------------------------
  1263. ;  BIN2DEC
  1264.  
  1265. ;  This routine converts a binary number, in AX:BX, to decimal notation.
  1266. ;  It will convert up to 8 digits, and will supress leading zeros.  The
  1267. ;  routine should be called with DS:SI set to point to the area to store
  1268. ;  the converted number.
  1269.  
  1270. Bin2Dec        proc    near
  1271.  
  1272.         push    es        ; save the registers
  1273.         push    ds
  1274.         push    di
  1275.         push    si
  1276.  
  1277.         mov    WorkAreaL,bx
  1278.         mov    WorkAreaH,ax    ; put the number on our scratchpad
  1279.  
  1280.         mov    ax,ds        ; point to the answer with ES:DI
  1281.         mov    es,ax
  1282.         mov    di,si
  1283.         add    di,7
  1284.  
  1285.         mov    si,offset WorkAreaL    ; point at scratchpad
  1286.  
  1287. Bin2DecLoop:    push    si
  1288.  
  1289.         xor    bx,bx        ; done flag
  1290.         mov    cx,2        ; 2 words in our number
  1291.         mov    dx,bx        ; clear remainder
  1292.         add    si,2        ; point to the high end
  1293.  
  1294. Bin2DecDigit:    push    cx        ; save word count
  1295.         mov    ax,[si]        ; get the digit
  1296.         mov    cx,10
  1297.         div    cx        ; convert it
  1298.         mov    [si],ax        ; store it back
  1299.         or    bx,ax        ; set the done flag appropriately
  1300.         sub    si,2        ; point to next lower
  1301.         pop    cx
  1302.         loop    Bin2DecDigit
  1303.  
  1304.         or    dl,'0'        ; make it into a decimal digit
  1305.         mov    [di],dl        ; and store it
  1306.         dec    di        ; adjust pointer
  1307.  
  1308.         pop    si        ; get the pointer back
  1309.         and    bx,bx        ; is the result zero?
  1310.         jne    Bin2DecLoop    ; nope!  Do more!
  1311.  
  1312.         pop    si        ; retrieve the used registers
  1313.         pop    di
  1314.         pop    ds
  1315.         pop    es
  1316.         ret
  1317.  
  1318. WorkAreaL    dw    0        ; low end of the work area
  1319. WorkAreaH    dw    0        ; high side of the work area
  1320.  
  1321. Bin2Dec        endp
  1322.  
  1323. InitBuffer      label byte              ; Use 1kB from here for init
  1324.  
  1325. driver        ends
  1326.         end
  1327.  
  1328. ; That's a wrap.
  1329. ; Special thanks to Bob Brody and Dr File Finder
  1330.  
  1331.